package cn.spdb.harrier.server.worker.exec;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.Date;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.atomic.AtomicInteger;

import cn.spdb.harrier.common.enmus.ExecutionStatus;
import cn.spdb.harrier.common.enmus.UdsJobType;
import cn.spdb.harrier.common.utils.DateUtils;
import cn.spdb.harrier.common.utils.Symbol;
import cn.spdb.harrier.dao.entity.UdsJob;
import cn.spdb.harrier.dao.utils.BeanContext;
import cn.spdb.harrier.server.entity.ComplementIns;
import cn.spdb.harrier.server.entity.JobExecutionContext;
import cn.spdb.harrier.server.worker.WorkerManagerThread;

public class ComplementRunable implements Runnable {

	private ComplementIns complementIns = null;

	private AtomicInteger integer = new AtomicInteger();

	private LinkedBlockingQueue<Integer> queue =new LinkedBlockingQueue<Integer>();
	
	
	public void offer() {
		queue.offer(integer.decrementAndGet());
	}

/**
 * 执行补数任务的核心方法。
 *
 * 该方法会根据配置的时间范围（startTime 到 endTime）逐天遍历，
 * 对每一天结合不同的 batch 值，判断当前 job 是否应该被触发执行。
 *
 * 触发条件包括：
 * - 多批次（multiBatch）与当前 batch 的匹配；
 * - 定时规则（cron 表达式）是否满足；
 * - 如果满足，则设置 jobDate 和 batch，并调用 exec(jobId) 提交执行。
 */
@Override
public void run() {
    // 用于构建 cron 表达式的临时缓冲区
    StringBuffer cron = new StringBuffer();

    // 遍历从开始时间到结束时间的每一天
    for (LocalDateTime start = complementIns.getUdsComplement().getStartTime();
         start.compareTo(complementIns.getUdsComplement().getEndTime()) <= 0;
         start = start.plusDays(1)) {

        LocalDate jobDate = start.toLocalDate(); // 当前处理日期

        // 遍历所有 batch 值
        for (int batch : complementIns.getbatchList()) {

            // 遍历所有需要执行的作业上下文
            for (JobExecutionContext jobExe : complementIns.getBeginJob()) {

                // 过滤不符合 multiBatch 和 batch 匹配规则的任务
                if ((jobExe.getUdsJob().getMultiBatch() == 0 && batch != 0)
                        || (jobExe.getUdsJob().getMultiBatch() != 0 && batch == 0)) {
                    continue;
                }

                // 将 LocalDate 转换为 Date 类型，用于后续判断
                Date date = DateUtils.localDateTime2Date(jobDate.atStartOfDay());

                // 根据作业类型判断是否满足调度条件
                if (jobExe.getJobType().equals(UdsJobType.D)) {
                    // 类型 D：直接通过，无需 cron 判断
                } else if (jobExe.getJobType().equals(UdsJobType.C)) {
                    // 类型 C：使用 timeCornList 中的表达式进行匹配
                    if (!jobExe.getTimeCornList().stream().map(mapper -> {
                        int i = 0;
                        cron.setLength(0);
                        // 构建完整的 cron 表达式（补充秒字段）
                        for (String sc : mapper.split(Symbol.KONG_GE)) {
                            if (i++ < 3) {
                                cron.append("0").append(Symbol.KONG_GE);
                            } else {
                                cron.append(sc).append(Symbol.KONG_GE);
                            }
                        }
                        return cron.toString();
                    }).anyMatch(predicate -> DateUtils.isSatisfiedBy(predicate, date))) {
                        continue; // 没有满足任何 cron 表达式，跳过
                    }
                } else {
                    // 其他类型：使用 cornList 中的表达式进行匹配
                    if (!jobExe.getCornList().stream()
                            .anyMatch(predicate -> DateUtils.isSatisfiedBy(predicate, date))) {
                        continue; // 没有满足任何 cron 表达式，跳过
                    }
                }

                // 设置作业的执行日期和 batch 号
                jobExe.getUdsJob().setJobDate(jobDate);
                jobExe.setBatch(batch);

                // 提交任务执行
                exec(jobExe.getJobId());
            }
        }
    }
}


/**
 * 提交指定节点的任务进行补数执行。
 *
 * 该方法首先从队列中获取许可（阻塞等待），用于控制最大并发任务数；
 * 然后根据当前节点是否有前置依赖，决定是否立即提交执行：
 * - 如果没有前置依赖，则直接创建并提交任务；
 * - 如果有前置依赖，则检查所有依赖是否满足条件（checkDependency）；
 *   满足后才提交当前任务，并递归提交后续任务。
 *
 * @param node 当前要执行的任务节点 ID
 */
public void exec(String node) {
    // 从队列中取出一个许可，控制最大并发任务数；若无许可则阻塞等待
    try {
        queue.take();
    } catch (InterruptedException e) {
        e.printStackTrace();
    }

    // 判断当前节点是否有前置依赖节点
    if (complementIns.getPrevious(node).isEmpty()) {
        // 无前置依赖，直接构建执行上下文并提交任务
        JobExecutionContext jec = complementIns.getDag().getNode(node);
        jec.setExecutionStatus(ExecutionStatus.RUNING); // 设置状态为运行中
        jec.setTaskInstanceId(BeanContext.getBean(WorkerManagerThread.class).getSingleId()); // 分配任务实例ID

        // 创建补数任务执行器
        ComplementExecuteRunnable cer = new ComplementExecuteRunnable(complementIns.getComplementId(), this);
        cer.setJobExecutionContext(jec);

        // 提交任务到线程池执行
        BeanContext.getBean(WorkerManagerThread.class).offer(cer);

        // 递归执行后续节点任务
        complementIns.getSubsequent(node).stream()
                     .forEach(action -> exec(action.getJobId()));
    } else {
        // 存在前置依赖，检查所有依赖是否满足
        if (complementIns.getPrevious(node).stream()
                         .allMatch(predicate -> checkDependency(node, predicate.getJobId()))) {

            // 所有依赖满足，构建并提交当前任务
            JobExecutionContext jec = complementIns.getDag().getNode(node);
            jec.setExecutionStatus(ExecutionStatus.RUNING);
            ComplementExecuteRunnable cer = new ComplementExecuteRunnable(complementIns.getComplementId(), this);
            cer.setJobExecutionContext(jec);
            BeanContext.getBean(WorkerManagerThread.class).offer(cer);

            // 递归执行后续节点任务
            complementIns.getSubsequent(node).stream()
                         .forEach(action -> exec(action.getJobId()));
        }
    }
}



	public ComplementRunable(ComplementIns complementIns) {
		this.complementIns = complementIns;
		for (int i = 0; i < complementIns.getUdsComplement().getMaxRunJob(); i++) {
			queue.offer(integer.incrementAndGet());
		}
	}

/**
 * 检查当前任务节点与其前置依赖节点之间的依赖关系是否满足执行条件。
 *
 * 该方法根据多批次（multiBatch）设置、作业日期（jobDate）、以及前置任务状态，
 * 判断当前任务是否可以安全地被触发执行。
 *
 * @param node    当前任务节点 ID
 * @param depNode 前置依赖任务节点 ID
 * @return 如果依赖满足，返回 true；否则返回 false
 */
private boolean checkDependency(String node, String depNode) {
    // 获取当前任务和依赖任务的 UdsJob 实例
    UdsJob job = complementIns.getDag().getNode(node).getUdsJob();
    UdsJob depJob = complementIns.getDag().getNode(depNode).getUdsJob();

    if (depJob.getMultiBatch() == 0) {
        // 依赖任务是单批次任务：只要其下一次调度时间在当前任务之前即可
        if (depJob.getNextJobDate().compareTo(job.getJobDate()) > 0) {
            return true;
        }
    } else {
        // 依赖任务是多批次任务
        if (job.getMultiBatch() == 0) {
            // 当前任务是单批次任务

            // 获取依赖边上的 batch 信息
            int depBatch = complementIns.getDag().getSubsequentEdgeInfo(node, depNode);

            // 判断依赖任务的 multiBatch 是否大于当前依赖边的 batch 数
            // 或者相等且上一次执行成功，则检查 nextJobDate 是否早于当前任务 jobDate
            if (depJob.getMultiBatch() > depBatch ||
                (depJob.getMultiBatch() == depBatch &&
                 depJob.getLastStatus().equals(ExecutionStatus.SUCCESS.name()))) {

                if (depJob.getNextJobDate().compareTo(job.getJobDate()) > 0) {
                    return true;
                }
            }

            // 如果依赖任务的 multiBatch 小于当前依赖边的 batch 数，
            // 并且依赖任务的 jobDate 在当前任务 jobDate 之前，也满足条件
            if (depJob.getMultiBatch() < depBatch &&
                depJob.getJobDate().compareTo(job.getJobDate()) > 0) {
                return true;
            }
        } else {
            // 当前任务也是多批次任务

            // 若依赖任务的 multiBatch 大于当前任务，
            // 或者两者相等且依赖任务上次执行成功，并且 nextJobDate 更早，则满足依赖
            if (depJob.getMultiBatch() > job.getMultiBatch() ||
                (depJob.getMultiBatch() == job.getMultiBatch() &&
                 depJob.getLastStatus().equals(ExecutionStatus.SUCCESS.name()))) {

                if (depJob.getNextJobDate().compareTo(job.getJobDate()) > 0) {
                    return true;
                }
            }

            // 如果依赖任务的 multiBatch 小于当前任务，
            // 但依赖任务的 jobDate 在当前任务 jobDate 之前，也满足依赖
            if (depJob.getMultiBatch() < job.getMultiBatch() &&
                depJob.getJobDate().compareTo(job.getJobDate()) > 0) {
                return true;
            }
        }
    }

    // 所有条件都不满足，依赖未达成
    return false;
}

	public LinkedBlockingQueue<Integer> getQueue() {
		return queue;
	}

	public void setQueue(LinkedBlockingQueue<Integer> queue) {
		this.queue = queue;
	}
}
